import React, { useEffect, useMemo, useRef, useState } from 'react'
import '../styles.css'
import { Terminal } from 'xterm'
import { FitAddon } from 'xterm-addon-fit'
import 'xterm/css/xterm.css'
import { useToast } from '../components/Toast'

type ServerRead = {
  id: number
  name: string
  host: string
  port: number
  username: string
  description?: string | null
}

export function TerminalPage({ server, onBack }: { server: ServerRead; onBack: () => void }): JSX.Element {
  const containerRef = useRef<HTMLDivElement | null>(null)
  const termRef = useRef<Terminal | null>(null)
  const fitRef = useRef<FitAddon | null>(null)
  const wsRef = useRef<WebSocket | null>(null)
  const [connected, setConnected] = useState(false)
  const [heartbeatMs, setHeartbeatMs] = useState<number>(20000)
  const toast = useToast()
  const remoteEchoRef = useRef(false)

  const sendInput = (data: string) => {
    if (wsRef.current && wsRef.current.readyState === WebSocket.OPEN) {
      wsRef.current.send(JSON.stringify({ type: 'input', data }))
    }
  }

  const sendLarge = (text: string) => {
    const CHUNK = 2048
    for (let i = 0; i < text.length; i += CHUNK) {
      sendInput(text.slice(i, i + CHUNK))
    }
  }

  const copySelection = async () => {
    try {
      const term = termRef.current
      if (!term) return
      const sel = term.getSelection()
      if (!sel) {
        toast.show('未选中文本', 'info')
        return
      }
      await navigator.clipboard.writeText(sel)
      toast.show('已复制到剪贴板', 'success')
    } catch {
      toast.show('复制失败', 'error')
    }
  }

  const pasteFromClipboard = async () => {
    try {
      if (!navigator.clipboard?.readText) {
        toast.show('浏览器不支持粘贴', 'error')
        return
      }
      const text = await navigator.clipboard.readText()
      if (!text) {
        toast.show('剪贴板为空', 'info')
        return
      }
      sendLarge(text)
    } catch {
      toast.show('粘贴失败', 'error')
    }
  }

  useEffect(() => {
    // 拉取后端设置，动态应用 WS 心跳间隔
    (async () => {
      try {
        const resp = await fetch('/api/settings/')
        if (resp.ok) {
          const cfg = await resp.json()
          const sec = Number(cfg?.ws_heartbeat_seconds)
          if (sec && sec > 0) setHeartbeatMs(sec * 1000)
        }
      } catch {}
    })()
  }, [])

  useEffect(() => {
    const term = new Terminal({
      cursorBlink: true,
      fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Consolas, monospace',
      theme: { background: '#111111' },
    })
    const fit = new FitAddon()
    term.loadAddon(fit)
    term.open(containerRef.current!)
    try { term.focus() } catch {}
    // 多次尝试 fit，确保渲染器就绪
    const tryFit = () => {
      try { fit.fit() } catch {}
    }
    requestAnimationFrame(tryFit)
    setTimeout(tryFit, 16)
    setTimeout(tryFit, 60)
    termRef.current = term
    fitRef.current = fit

    const url = new URL(`/ws/ssh/${server.id}`, window.location.origin)
    url.protocol = url.protocol.replace('http', 'ws')
    // 初始尺寸通过 onopen 后发送
    const ws = new WebSocket(url.toString())
    wsRef.current = ws

    ws.onopen = () => {
      // 立即发送一次 resize（如果服务端还在准备，会在那边做缓冲）
      tryFit()
      ws.send(JSON.stringify({ type: 'resize', cols: term.cols, rows: term.rows }))
    }
    ws.onmessage = (ev) => {
      try {
        const msg = JSON.parse(ev.data)
        if (msg.type === 'ready') {
          setConnected(true)
          tryFit()
          ws.send(JSON.stringify({ type: 'resize', cols: term.cols, rows: term.rows }))
        } else if (msg.type === 'accepted') {
          // 握手阶段，提示连接中
          term.writeln('\r\n[正在建立 SSH 连接...]')
        } else if (msg.type === 'data') {
          remoteEchoRef.current = true
          term.write(msg.data)
        } else if (msg.type === 'error') {
          term.writeln(`\r\n[错误] ${msg.message}`)
        } else if (msg.type === 'pong') {
          // ignore
        }
      } catch {
        // 兼容服务端原始输出（非 JSON）
        remoteEchoRef.current = true
        term.write(ev.data)
      }
    }
    ws.onerror = () => {
      term.writeln('\r\n[连接错误]')
    }
    ws.onclose = () => {
      setConnected(false)
      term.writeln('\r\n[连接已关闭]')
    }
    term.onData((data) => {
      if (ws.readyState === WebSocket.OPEN) {
        ws.send(JSON.stringify({ type: 'input', data }))
      }
      // 本地回显兜底：在未收到任何远端数据前，显示输入内容，避免“看不见输入”
      if (!remoteEchoRef.current) {
        // 处理退格
        if (data === '\u007f') { // Backspace
          // 移动光标并清除一个字符
          term.write('\b \b')
        } else {
          term.write(data)
        }
      }
    })

    const onResize = () => {
      try { fit.fit() } catch {}
      if (ws.readyState === WebSocket.OPEN) {
        ws.send(JSON.stringify({ type: 'resize', cols: term.cols, rows: term.rows }))
      }
    }
    window.addEventListener('resize', onResize)

    return () => {
      window.removeEventListener('resize', onResize)
      ws.close()
      term.dispose()
    }
  }, [server.id])

  // 独立的心跳 effect：根据后端配置动态调整，无需重建终端
  useEffect(() => {
    if (!connected || !wsRef.current) return
    const tick = () => {
      const ws = wsRef.current
      if (ws && ws.readyState === WebSocket.OPEN) {
        ws.send(JSON.stringify({ type: 'ping' }))
      }
    }
    const id = setInterval(tick, heartbeatMs)
    return () => clearInterval(id)
  }, [connected, heartbeatMs])

  return (
    <div className="terminal-root">
      <div className="panel terminal-toolbar" style={{ padding: 10, display: 'flex', alignItems: 'center', gap: 10 }}>
        <button className="btn" onClick={onBack}>返回</button>
        <div className="muted">
          {server.name} · {server.username}@{server.host}:{server.port}
        </div>
        <div className="spacer" />
        <button className="btn" onClick={copySelection}>复制选中</button>
        <button className="btn" onClick={pasteFromClipboard}>粘贴</button>
        <div style={{ color: connected ? 'lightgreen' : 'salmon' }}>{connected ? '已连接' : '未连接'}</div>
      </div>
      <div className="terminal-container">
        <div
          ref={containerRef}
          className="terminal-pane"
          onClick={() => {
            try { termRef.current?.focus() } catch {}
          }}
          style={{ cursor: 'text' }}
        />
      </div>
    </div>
  )
}

